import sys
sys.path.append('/opt/lib/python3.11/site-packages')
import json
import requests
from pypinyin import pinyin, Style
import xml.etree.ElementTree as ET
import os

class KodiPinyinUpdater:
    def __init__(self, host='localhost', port=8080, username=None, password=None, 
                 guisettings_path=None, totals_file='~/.kodi_pinyin_totals.json'):
        # 自动获取guisettings.xml路径
        if not guisettings_path:
            default_path = os.path.expanduser("~/.kodi/userdata/guisettings.xml")
            if os.path.exists(default_path):
                guisettings_path = default_path
            else:
                raise FileNotFoundError("未找到默认的guisettings.xml文件，请手动指定路径")

        # 解析XML获取用户名和密码
        self.username, self.password = self._parse_guisettings(guisettings_path)
        
        self.url = f"http://{host}:{port}/jsonrpc"
        self.auth = (self.username, self.password)
        self.marker = "#_#"
        self.totals_file = os.path.expanduser(totals_file)

    def _parse_guisettings(self, path):
        """解析guisettings.xml获取Web服务器凭据"""
        try:
            tree = ET.parse(path)
            root = tree.getroot()
            
            # 定位用户名和密码节点
            username_node = root.find(".//setting[@id='services.webserverusername']")
            password_node = root.find(".//setting[@id='services.webserverpassword']")
            
            username = username_node.text if username_node is not None else "kodi"
            password = password_node.text if password_node is not None else ""
            
            return username, password
        except Exception as e:
            raise ValueError(f"解析guisettings.xml失败: {str(e)}")

    def get_pinyin_initials(self, text):
        """生成首拼首字母"""
        if not text:
            return ""
        initials = pinyin(text, style=Style.FIRST_LETTER)
        pinyin_part = ''.join([i[0].upper() for i in initials if i])
        return f"{pinyin_part}{self.marker}{text}"

    def call_api(self, method, params):
        """通用API调用方法"""
        try:
            response = requests.post(
                self.url,
                auth=self.auth,
                headers={'Content-Type': 'application/json'},
                data=json.dumps({
                    "jsonrpc": "2.0",
                    "id": 1,
                    "method": method,
                    "params": params
                })
            )
            return response.json()
        except Exception as e:
            print(f"API调用失败: {str(e)}")
            return None

    def get_totals(self):
        """获取当前媒体库总数"""
        try:
            # 获取电影总数
            movies = self.call_api("VideoLibrary.GetMovies", {"properties": []})
            if not movies or 'result' not in movies:
                raise ValueError("获取电影总数失败")
            movie_total = movies['result'].get('limits', {}).get('total', 0)

            # 获取电视剧总数
            tvshows = self.call_api("VideoLibrary.GetTVShows", {"properties": []})
            if not tvshows or 'result' not in tvshows:
                raise ValueError("获取电视剧总数失败")
            tvshow_total = tvshows['result'].get('limits', {}).get('total', 0)

            return movie_total, tvshow_total
        except Exception as e:
            raise RuntimeError(f"获取媒体总数失败: {str(e)}")

    def load_totals(self):
        """从本地文件加载上次记录的总数"""
        try:
            with open(self.totals_file, 'r') as f:
                data = json.load(f)
                return data.get('movie_total', 0), data.get('tvshow_total', 0)
        except (FileNotFoundError, json.JSONDecodeError):
            return 0, 0

    def save_totals(self, movie_total, tvshow_total):
        """保存当前总数到本地文件"""
        with open(self.totals_file, 'w') as f:
            json.dump({
                'movie_total': movie_total,
                'tvshow_total': tvshow_total
            }, f)

    def process_media(self, media_type):
        """通用处理逻辑（支持movie/tvshow）"""
        # 确定API方法
        get_method = f"VideoLibrary.Get{media_type.capitalize()}s"
        set_method = f"VideoLibrary.Set{media_type.capitalize()}Details"
        id_field = f"{media_type}id"  # movieid/tvshowid

        # 分页参数配置
        page_size = 200  # 每页最大处理量
        current_offset = 0
        total_updated = 0

        while True:
            # 动态计算请求范围
            request_params = {
                "properties": ["title", "originaltitle"],
                "filter": {
                    "field": "originaltitle",
                    "operator": "doesnotcontain",
                    "value": self.marker
                },
                "limits": {"start": current_offset, "end": current_offset + page_size},
                "sort": {"order": "ascending", "method": "title"}  # 增加稳定排序
            }

            # API请求
            result = self.call_api(get_method, request_params)

            # 错误处理
            if not result or 'result' not in result:
                print(f"获取{media_type}数据失败，当前偏移量：{current_offset}")
                break

            items = result['result'].get(f"{media_type}s", [])
            if not items:
                break  # 无更多数据时退出循环

            # 处理当前页数据
            page_updated = 0
            for item in items:
                # 跳过已处理项（二次验证）
                if self.marker in item.get('originaltitle', ''):
                    continue

                # 生成拼音标记
                new_value = self.get_pinyin_initials(item.get('title', ''))

                # 更新记录
                update_result = self.call_api(set_method, {
                    id_field: item[id_field],
                    "originaltitle": new_value
                })

                # 结果验证
                if update_result and 'error' not in update_result:
                    page_updated += 1
                else:
                    print(f"更新失败：{item.get('title')} | 响应：{json.dumps(update_result)}")
            # 动态调整偏移量
            actual_count = len(items)
            current_offset += actual_count  # 根据实际返回数量推进偏移量
            total_updated += page_updated
        
            print(f"当前偏移量：{current_offset} | 本页更新：{page_updated} | 累计更新：{total_updated}")
            
             # 提前退出条件：当实际返回数量小于请求量时
            if actual_count < page_size:
                break
        return total_updated
    def run(self):
        """主运行逻辑"""
        try:
            # 获取当前总数
            current_movie_total, current_tvshow_total = self.get_totals()
            # 读取上次记录
            last_movie_total, last_tvshow_total = self.load_totals()

            # 检查总数是否变化
            if current_movie_total == last_movie_total and current_tvshow_total == last_tvshow_total:
                print("媒体库总数未变化，无需更新")
                return

            # 执行更新
            movie_count = self.process_media('movie')
            tvshow_count = self.process_media('tvshow')

            # 保存新总数
            self.save_totals(current_movie_total, current_tvshow_total)

            summary_message = f"更新完成！电影：{movie_count}条，电视剧：{tvshow_count}条"
            print(summary_message)

        except Exception as e:
            print(f"运行错误: {str(e)}")

if __name__ == '__main__':
    try:
        # 使用示例（根据实际情况修改参数）
        updater = KodiPinyinUpdater(
            host='127.0.0.1',
            port=8080,
            guisettings_path="/storage/.kodi/userdata/guisettings.xml"  # 可选自定义路径
        )
        updater.run()
    except Exception as e:
        print(f"初始化失败: {str(e)}")